#pragma once

// Copyright (c) 2010 Manuel Peinado Gallego <manuel.peinado@gmail.com>
// Distributed under the MIT license


#include <alg3d/alg3d.h>
#include "NodeTree.h"
#include "NodeEvent.h"
#include <string>
#include "Pickable.h"
class Sphere;
class AABB;
class NodeManager;
class View3d;
class Node;


// ----------------------------------------------------------------------------
// Class "Node"
// ----------------------------------------------------------------------------
// Represents a node in our scene tree. It's the (virtual) base class for
//   all the different kinds of nodes
// ----------------------------------------------------------------------------
// Services provided by an object of this class:
// * Draw it, (but only if canBeDrawn() return true
// * Get its bounding box or sphere, but only if isBounded() returns true
// * Get its local or global transform (but the local transform is usually 
//   the identity matrix unless the node is a subclass of TransformNode)
// * Traverse the list of its children nodes
// * Get its parent node or the parent transform of its parent node
// * Get its associated shared ptr (there's always one, provided that
//   the node has been attached to a NodeManager)
// * Get or set its name
// * Draw it, but only if canBeDrawn() returns true
// ----------------------------------------------------------------------------

class Node : public Pickable, boost::noncopyable
{    
public:
    
    // --------------------------------------------------------------
    
    // It does not make much sense to have instances of this class,
    // but we use one for the root node (it serves as a dummy node)
    Node();

    // Does nothing special, besides being virtual
    virtual ~Node();

    // --------------------------------------------------------------
    
    // If a node Nk has as ancestors the nodes N0, N1, ..., N{k-1}, 
    // where N0 is the root of the scene tree, and Li is the local
    // transform of node Ni, then the global transform of Nk is 
    // Gk =  L0 * L1 * ... * L{k-1} * Lk. In terms of its parent 
    // global transform: Gk=G{k-1} * Lk. This is costly to compute
    // to comopute, that's why we cache it in the m_globalTransform
    // member
    gx::Mat4 getGlobalTransform() const;

    // --------------------------------------------------------------

    // Iterators to the begin and end of the list of children of 
    // this node. Please note that a NodeSiblingIterator is different from
    // a NodeIterator. In particular, be careful with code such
    // as the following which compiles but doesn't work as expected
    // at runtime:
    // NodeIterator it = someNode->getChildrenBegin();
    // for(; it != someNode->getChildrenEnd(); ++it)
    //     // do something with *it
    // Note that we've mistakenly used NodeIterator instead of 
    // ChildNodeIt
    NodeSiblingIterator getChildrenBegin() const;
    NodeSiblingIterator getChildrenEnd() const;

    // Returns the parent node of this node (NULL if this is
    // node is the root of the scene tree)
    Node* getParent() const;

    // Returns the global transform of the parent node (this is
    // basically a shortcut for getParent()->getGlobalTransform())
    // If the node is the root node, the identity matrix is returned
    gx::Mat4 getParentGlobalTransform() const;

    // --------------------------------------------------------------

    // Is the node bounded? Node bounding volumes are
    // using for things like mouse picking
    bool isBounded() const { return isBoundedImpl(); }
    
    // Only call these if isBounded() returns true
    AABB getAabbLocal() const;

    // --------------------------------------------------------------

    // Is the node drawable (visible)?
    // You should check this before calling draw()
    bool canBeDrawn() const { return canBeDrawnImpl(); }

    void drawAabb() const;

    // --------------------------------------------------------------
    // Drawable overrides

    bool isSelectable() const override
    {
        return true;
    }

    // "Drawable" override
    // Calling this member function for an object for which
    // canBeDrawn() returns false is a runtime error
    // A Node is a Drawable but nodes are not registerd as drawables
    // with the views. Instead, the node manager (itself a drawable)
    // is registered and takes care of drawing its nodes, using the
    // same Drawable interface. The main advantage of this convoluted
    // architecture is that we reuse the infrastructure for picking
    // provided by the drawable class
    void draw(View3d * view) const override;

    // --------------------------------------------------------------
    // Serialization (Selectable overrides)

    virtual void read(std::wistream& is) override;
    virtual void write(std::wostream& os) const override;
    
    // --------------------------------------------------------------

protected:

    // --------------------------------------------------------------

    // Note to derived classes: if you override canBeDrawnImpl() so
    // that it returns true, you should also override draw (otherwise
    // you'll trigger a runtime assertion)

    // Returns false. Override and return true if the subclass is drawable
    virtual bool canBeDrawnImpl() const;
    
    // --------------------------------------------------------------

    // Note to derived classes: if you override isBounded() so
    // that it returns true, you should also override getBSphLoclImpl() 
    // and getAbbbLocImpl (otherwise you'll trigger a runtime assertion)

    // Default implementation returns false. Override this if your 
    // subclass is bounded (this is necessary, for instance, to make
    // it participate in the picking mechanism)
    virtual bool isBoundedImpl() const;

    virtual AABB getAabbLocalImpl() const;

    // --------------------------------------------------------------
    
    // This methods might seem weird here but it's necessary to allow
    // subclass that want to implement a caching strategy for their
    // computed global transform (see class TransformNode)

    // Called when the local transform of an ancestor changes. Calls
    // the same method on this node's children so that the notifica-
    // on is propagated to the leaves. So the process is as follows:
    // each time a node chages its local transform, it calls 
    // invalidateGlobalTransform on itself, which in turn calls the
    // same member function on all of its children, and so on until
    // all the nodes in the subtree of the original node have been
    // notified
    void invalidateGlobalTransform() const;

    // --------------------------------------------------------------

    // Used by the node manager (who has access because it's a friend)
    // Every time a new node is added to the scene tree, an iterator
    // is created for that node and the node manager associates that
    // iterator to the node by calling this function. The iterator
    // can then be retrieved using "getTreeIt". If you create a node
    // but don't add it to the scene tree, its iterator will not be
    // set and so the node will be interpreted as the root of a scene
    // tree consisting of that node alone (e.g., getParent() will 
    // return false and so on). The only operation that is invalid
    // (and triggers and assertion) is calling getTreeIt for such
    // a node. So if you're not absolutely sure about whether a node
    // has an associated iterator (i.e., is part of a scene tree) you
    // are advised to call hasValidIt() before calling getTreeIt().

    NodeIterator getIterator() const;
    bool hasValidIterator() const;
    void setIterator(NodeIterator it);

    // --------------------------------------------------------------

    // Grants subclasses access to the node manager
    NodeManager* getNodeManager() const { return m_nodeManager; }

    // Called by the NodeManager when the materials is registered
    // with it. Thanks to this function, the relation is two-way
    void setNodeManager(NodeManager* nm) {  m_nodeManager = nm; }

    // --------------------------------------------------------------
    
    // Convenience function to save subclasses from writing some
    // of errorprone boilerplate 
    void notifyPropertyChange(NodeEvent::Property=NodeEvent::OtherProperty);

private:

    // We make the node manager a friend so that:
    //   * NodeManager::reparent can call invalidateGlobalTransform
    //   * NodeManager::appendNode can call set/getTreeIt
    friend class NodeManager;

    // --------------------------------------------------------------

    // See comment in members invalidateGlobalTransform and 
    // isGlobalTransformValid
    mutable bool m_globalTransformReady;
    mutable gx::Mat4 m_globalTransform; // = parent->Global * this->local

    // --------------------------------------------------------------

    // See comment in constructor
    NodeManager* m_nodeManager;

    // --------------------------------------------------------------

    NodeIterator m_iterator;
    // --------------------------------------------------------------

};


